package io.gitee.hechenl.fastiotreport.util;

import com.udojava.evalex.Expression;
import io.gitee.hechenl.fastiotreport.domain.model.ConfigMetric;
import io.gitee.hechenl.fastiotreport.enums.EnergyCalculatePattern;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.regex.Pattern;

public class ExprCalculator {


    @FunctionalInterface
    public interface EnergySupplier {
        BigDecimal get(List<String> pointIds);
    }

    @FunctionalInterface
    public interface ProductSupplier {
        BigDecimal get(String unit);
    }

    private static String getEnergyCalculatePattern(String metricExpr) {
        if (!metricExpr.contains("/")) {
            return metricExpr;
        } else {
            return metricExpr.split("/")[0];
        }
    }

    private static BigDecimal getEnergyCalculateFactor(ConfigMetric configMetric) {
        if (!configMetric.getMetricExpr().contains("/")) {
            return BigDecimal.valueOf(1);
        }
        return new BigDecimal(configMetric.getMetricExpr().split("/")[1]);
    }

    private static String getEnergyCalculateProduct(ConfigMetric configMetric) {
        if (!configMetric.getMetricExpr().contains("/")) {
            return null;
        } else {
            return configMetric.getMetricExpr().split("/")[2];
        }
    }

    public static BigDecimal calculateMetric(
            ConfigMetric configMetric,
            EnergySupplier energySupplier,
            ProductSupplier productSupplier) {
        Expression expression = new Expression(configMetric.getMetricExprCal());
        Map<String, BigDecimal> expressionValueMap
                = getExpressionValue(getEnergyCalculatePattern(configMetric.getMetricExpr()), energySupplier);
        expressionValueMap.forEach(expression::with);
        BigDecimal factor = getEnergyCalculateFactor(configMetric);
        String unit = getEnergyCalculateProduct(configMetric);
        BigDecimal product = Optional.ofNullable(productSupplier.get(unit)).orElse(BigDecimal.valueOf(1));
        return expression.eval().multiply(factor).divide(product, 2, RoundingMode.HALF_UP);
    }

    /*public static void main(String[] args) {
        String expr = "(2102 + 2106 + 2108 + 2093 + 2114 + 2076 + 2098 + 2065 + 2094 + 2095 + 2100 + 2071 + 2097 + 2252 + 2066 + 2068 + 2072 + 2080 + 2067 + 2104 + 2105 + 2099 + 2127 + 2124 + 2294 + 2297)-(2298 + 2064 + 2266 + 2267 + 2268)";
        Pattern compile = Pattern.compile("^\\(\\d+(\\+\\d+)+\\)-\\(\\d+(\\+\\d+)+\\)$");
        if (compile.matcher(expr.replace(" ", "")).matches()) {
            System.out.println("ok");
        }
    }*/

    /**
     * 获取计算表达式
     */
    public static String getCalExpr(String metricExpr) {
        String energyCal = getEnergyCalculatePattern(metricExpr.replace(" ", ""));
        if (Pattern.compile(EnergyCalculatePattern.energy1.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy1.getCalExpr();
        } else if (Pattern.compile(EnergyCalculatePattern.energy2.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy2.getCalExpr();
        } else if (Pattern.compile(EnergyCalculatePattern.energy3.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy3.getCalExpr();
        } else if (Pattern.compile(EnergyCalculatePattern.energy4.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy4.getCalExpr();
        } else if (Pattern.compile(EnergyCalculatePattern.energy5.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy5.getCalExpr();
        } else if (Pattern.compile(EnergyCalculatePattern.energy6.getPattern()).matcher(energyCal).matches()) {
            return EnergyCalculatePattern.energy6.getCalExpr();
        } else {
            return null;
        }
    }

    private static Map<String, BigDecimal> getExpressionValue(
            String formula, EnergySupplier energySupplier) {
        Map<String, BigDecimal> map = new HashMap<>();
        List<String> bracketItems = RegexUtil.extractBracketItems(formula);
        for (int i = 0; i < bracketItems.size(); i++) {
            List<String> pointIds = RegexUtil.extractBracketNumber(bracketItems.get(i));
            map.put("C" + i, Optional.ofNullable(energySupplier.get(pointIds)).orElse(BigDecimal.ZERO));
        }
        return map;
    }
}